package main

/*
#cgo CFLAGS: -I${SRCDIR}/library
#include "library.h"
*/
import "C"
import (
	"database/sql"
	"fmt"
	"gitee.com/hakwolf/dbmagic/golib/internal/models"
	"gitee.com/hakwolf/dbmagic/golib/internal/routers/database"
	sqldriver2 "gitee.com/hakwolf/dbmagic/golib/internal/sqldriver"
	"strconv"
	"strings"
	"unsafe"
)

//export GetSqlDbConn
func GetSqlDbConn(Id C.int, ret *C.struct_MYSQL) {
	//func Test(dbinfo *C.struct_DbInfo) *C.struct_ResultInfo {
	//var
	//cStruct.field1 = 10
	//cString := C.CString("Hello from C")
	//cStruct.field2 = (*C.char)(cString)

	//fmt.Printf("Field1: %d\n", cStruct.field1)
	//fmt.Printf("Field2: %s\n", C.GoString(dbinfo.server))
	//server := C.GoString(dbinfo.server) //C.GoString(dbinfo.server)
	//gouser := C.GoString(dbinfo.user)
	//pwd := C.GoString(dbinfo.pwd)
	//godb := C.GoString(dbinfo.db)
	//goport := int(dbinfo.port)

	hostModel := new(models.DHost)
	hostInfo, err := hostModel.Detail(int(Id))
	if err == nil {
		var sqlDb *sql.DB
		switch hostInfo.Databasetype {
		case database.DSqlserver:
			lvPort := int(hostInfo.Port)
			sqlDb, err = sqldriver2.GetDB(&hostInfo.Host, &hostInfo.User, &hostInfo.Password, &hostInfo.Database, &lvPort)
			//sqlDb.SetMaxIdleConns()
			//sqlDb.SetMaxOpenConns()
			//sqlDb.SetConnMaxLifetime()
		case database.DMysql:
			sqlDb, err = database.GetDbEng("mysql", hostInfo.Host, hostInfo.User, hostInfo.Password, "mysql", int(hostInfo.Port))

		}
		if err == nil {
			//code = C.CString(err.Error())
			//fmt.Println(sqldb)
			//ptr := unsafe.Pointer(sqlDb)
			//// 将结构体地址转换为整型
			//addr := uintptr(ptr)
			//
			////return C.struct_ResultInfo{strId: C.CString(strconv.Itoa(int(addr))), ers: C.CString("nil")} //, C.CString()
			//
			//ret.strId = C.CString(strconv.Itoa(int(addr)))
			//ret.ers = C.CString("nil")
			ret.connectID = C.uint64_t(NewMapObj(sqlDb))

			return

		} else {
			str := C.CString(err.Error())
			//return C.struct_ResultInfo{strId: C.CString(strconv.Itoa(int(0))), ers: str} //, C.CString()
			//ret.strId = C.CString(strconv.Itoa(int(0)))
			//ret.ers = str
			ret.connectID = 0
			C.setdberror(ret, str, nil)
			return

		}

	}

}

//export qiusql_real_query
func qiusql_real_query(res *C.struct_MYSQL, input *C.char) C.uint32_t { //, rowsID *C.uint64_t
	s := GetMapObj(uint64(res.connectID))
	db := s.(*sql.DB)
	rows, err := db.Query(C.GoString(input))

	//rows.NextResultSet()

	if err == nil {
		res.rowsID = C.uint64_t(NewMapObj(rows))
		return 0

	} else {
		fmt.Println("query: ", err)
		str := C.CString(err.Error())
		res.rowsID = 0
		C.setdberror(res, str, nil)
		return 1
	}
}

//export qiusql_next_result
func qiusql_next_result(res *C.struct_MYSQL) C.uint32_t {
	s := GetMapObj(uint64(res.rowsID))
	rows := s.(*sql.Rows)

	if rows != nil {
		if rows.NextResultSet() {
			return 0
		} else {
			return 1
		}
	}
	C.setdberror(res, C.CString("rowsID is null"), nil)
	return 2
}

//export qiusql_store_result
func qiusql_store_result(res *C.struct_MYSQL) *C.struct_MYSQL_RES { //strID *C.char

	//addr, _ := strconv.Atoi(C.GoString(strID))
	//fmt.Printf("addr= %d", addr)
	//fmt.Println()
	//rows := (*sql.Rows)(unsafe.Pointer(uintptr(addr)))

	s := GetMapObj(uint64(res.rowsID))
	rows := s.(*sql.Rows)
	// 获取列信息
	columns, err := rows.Columns()
	if err != nil {
		//log.Fatal(err)
		return nil
	}

	// 准备接收数据的变量
	values := make([]sql.RawBytes, len(columns))
	scanArgs := make([]interface{}, len(values))
	for i := range values {
		scanArgs[i] = &values[i]
	}

	//var result *C.struct_MYSQL_RES
	var (
	//to       *C.char
	//end_to   *C.char
	//cur      *C.struct_MYSQL_ROWS
	//prev_ptr **C.struct_MYSQL_ROWS
	)

	leng := C.int(len(columns))
	result := C.setMemMYSQLRES(leng)
	if result == nil {
		return nil
		// 使用分配的内存

	}

	result.field_count = C.uint(len(columns))

	resData := C.setMemMYSQLDATA() //C.malloc(C.size_t(unsafe.Sizeof(C.struct_MYSQL_DATA{})))
	resData.rows = 0
	resData.fields = result.field_count //need

	prev_ptr := &resData.data

	// 遍历查询结果
	for rows.Next() {
		resData.rows++
		cur := C.setMemMYSQL_ROWS()        //C.malloc(C.size_t(unsafe.Sizeof(C.struct_MYSQL_ROWS{})))
		cur.data = C.setMemMYSQL_ROW(leng) //C.malloc((result.field_count + 1) * C.size_t(unsafe.Sizeof(C.char)))

		*prev_ptr = cur
		prev_ptr = &cur.next

		err = rows.Scan(scanArgs...)
		if err != nil {
			//rerMsg = err.Error()
			//return C.struct_ResultArrayInfo{ers: C.CString(erMsg), leng: C.int(len), strArr: list}
			return nil
		}
		// 根据列类型转换数据
		var v []byte
		for i, col := range values {
			if col == nil {
				//v = []byte("NULL")
				//cur.data[i] = nil
				C.setROWSvalue(cur, C.int(i), nil)
			} else {
				//v = col
				C.setROWSvalue(cur, C.int(i), C.CString(string(v)))
			}
			fmt.Println(columns[i], ": ", string(v))
			//C.setlistvalue(list, C.int(i), C.CString(string(v)))
			//len++
		}
		fmt.Println("nil")
		//erMsg = "nil"
	}
	*prev_ptr = nil

	result.data = resData
	result.row_count = result.data.rows
	result.data_cursor = result.data.data

	fields := C.setMemMYSQL_FIELD(leng)

	columnTypes, errType := rows.ColumnTypes()
	if errType == nil {
		for _, columnType := range columnTypes {
			fields.name = C.CString(columnType.Name())
			length, ok := columnType.Length()
			if ok {
				fields.length = C.ulong(length)
			} //返回十进制类型的小数位数和精度
			precision, scale, ok := columnType.DecimalSize() //returns the scale and precision of a decimal type
			if ok {
				fields.precision = C.ulong(precision)
				fields.scale = C.ulong(scale)
			}
			C.MYSQL_FIELD_inc(fields)
		}
		result.fields = fields
	} else {
		C.free(unsafe.Pointer(fields))
		result.fields = nil
	}

	return result
	//var arayinfo C.struct_ResultArrayInfo
	//arayinfo = C.struct_ResultArrayInfo{}
	//arayinfo.strArr = list
	//arayinfo.leng = C.int(len)
	//arayinfo.ers = C.CString(erMsg)

	//return arayinfo //, C.CString()

}

//export qiusql_free_result
func qiusql_free_result(res *C.struct_MYSQL_RES) {
	C.mysql_free_result(res)
}

//export qiusql_fetch_row
func qiusql_fetch_row(res *C.struct_MYSQL_RES) C.MYSQL_ROW {
	return C.mysql_fetch_row(res)
}

//export qiusql_data_seek
func qiusql_data_seek(res *C.struct_MYSQL_RES, row C.uint64_t) {
	C.mysql_data_seek(res, row)
}

//export qiusql_init
func qiusql_init(res *C.struct_MYSQL) *C.struct_MYSQL {
	return C.mysql_init(res)
}

//export qiusql_num_fields
func qiusql_num_fields(res *C.struct_MYSQL_RES) C.uint32_t {
	return C.mysql_num_fields(res)
}

//export qiusql_num_rows
func qiusql_num_rows(res *C.struct_MYSQL_RES) C.uint64_t {

	return C.mysql_num_rows(res)
}

//export qiusql_affected_rows
func qiusql_affected_rows(res *C.struct_MYSQL) C.uint64_t {
	return res.affected_rows //需要各种数据库查返回影响行数
}

//export qiusql_errno
func qiusql_errno(res *C.struct_MYSQL) C.uint32_t {
	return C.mysql_errno(res)
}

//export qiusql_error
func qiusql_error(res *C.struct_MYSQL) *C.char {
	return C.mysql_error(res)
}

//export RowNext
func RowNext(strID *C.char) C.struct_ResultArrayInfo {

	addr, _ := strconv.Atoi(C.GoString(strID))
	fmt.Printf("addr= %d", addr)
	fmt.Println()
	rows := (*sql.Rows)(unsafe.Pointer(uintptr(addr)))

	var erMsg string

	// 获取列信息
	columns, err := rows.Columns()
	if err != nil {
		//log.Fatal(err)
		return C.struct_ResultArrayInfo{strArr: nil, ers: C.CString(err.Error())}
	}

	// 准备接收数据的变量
	values := make([]sql.RawBytes, len(columns))
	scanArgs := make([]interface{}, len(values))
	for i := range values {
		scanArgs[i] = &values[i]
	}

	list := C.setlist(C.int(len(columns)))

	var len int
	// 遍历查询结果
	if rows.Next() {

		err = rows.Scan(scanArgs...)
		if err != nil {
			erMsg = err.Error()
			return C.struct_ResultArrayInfo{ers: C.CString(erMsg), leng: C.int(len), strArr: list}
		}
		// 根据列类型转换数据
		var v []byte
		for i, col := range values {
			if col == nil {
				v = []byte("NULL")
			} else {
				v = col
			}
			fmt.Println(columns[i], ": ", string(v))
			C.setlistvalue(list, C.int(i), C.CString(string(v)))
			len++
		}
		fmt.Println("nil")
		erMsg = "nil"
	} else {
		erMsg = "eof"
		fmt.Println("查询结束")
	}
	var arayinfo C.struct_ResultArrayInfo
	arayinfo = C.struct_ResultArrayInfo{}
	arayinfo.strArr = list
	arayinfo.leng = C.int(len)
	arayinfo.ers = C.CString(erMsg)

	return arayinfo //, C.CString()
	//return nil
}

//export DLStore
func DLStore(form C.struct_SqlHost, ret *C.struct_ResultInfo) { //(error, int)
	hostModel := new(models.DHost)
	id := int(form.Id)

	nameExist, err := hostModel.NameExists(C.GoString(form.Name), int(form.Id))
	//var err2 error
	if err != nil {
		//err2 = errors.New("操作失败")
		//return err2, id //return json.CommonFailure("操作失败", err)
		//return C.struct_ResultInfo{strId: C.CString("0"), ers: C.CString("操作失败")}
		ret.strId = C.CString("0")
		ret.ers = C.CString("操作失败")
		return
	}
	if nameExist {
		//err2 = errors.New("实例名已存在")
		//return C.struct_ResultInfo{strId: C.CString("0"), ers: C.CString("实例名已存在")}
		ret.strId = C.CString("0")
		ret.ers = C.CString("实例名已存在")
		return
	}
	//
	hostModel.Name = strings.TrimSpace(C.GoString(form.Name))
	////hostModel.Alias = strings.TrimSpace(form.Alias)
	hostModel.Port = int64(form.Port)
	hostModel.Remark = strings.TrimSpace(C.GoString(form.Remark))
	hostModel.User = C.GoString(form.User)
	hostModel.Host = C.GoString(form.Host)
	hostModel.Password = C.GoString(form.Password)
	hostModel.Databasetype = int(form.Databasetype)
	hostModel.Version = C.GoString(form.Version)
	hostModel.Locate = C.GoString(form.Locate)
	hostModel.Database = C.GoString(form.Database)
	hostModel.Sqlpath = C.GoString(form.Sqlpath)
	hostModel.Local = int(form.Local)
	hostModel.Wmode = int(form.Wmode)
	isCreate := false

	if id > 0 {
		_, err = hostModel.UpdateBean(id)
	} else {
		isCreate = true
		id, err = hostModel.Create()
	}
	if err != nil {
		//err2 = errors.New("保存失败！")

		//return C.struct_ResultInfo{strId: C.CString("0"), ers: C.CString("保存失败")}
		ret.strId = C.CString("0")
		ret.ers = C.CString("保存失败")
		return
	}

	if !isCreate {

	}

	ret.strId = C.CString(strconv.Itoa(id))
	ret.ers = C.CString("nil")

	//defer func() {
	//	C.free(unsafe.Pointer(ret.strId))
	//	C.free(unsafe.Pointer(ret.ers))
	//}()

	return
	//return C.struct_ResultInfo{strId: C.CString(strconv.Itoa(id)), ers: C.CString("nil")}
}

//export FreeResultInfo
func FreeResultInfo(ret *C.struct_ResultInfo) { //用ret *C.struct_ResultInfo可以作为返回参数
	C.free(unsafe.Pointer(ret.strId))
	C.free(unsafe.Pointer(ret.ers))
}

// All 获取所有主机
//
//export DLAll
func DLAll() C.struct_ResultArraySQLInfo { //[]models.DHost
	var erMsg string
	var ResultSqlinfo C.struct_ResultArraySQLInfo
	var leng int
	hostModel := new(models.DHost)
	hostModel.PageSize = -1
	hosts, err := hostModel.List(models.CommonMap{})
	if err != nil {
		//logger.Error(err)
		//return nil
		erMsg = err.Error()
		ResultSqlinfo = C.struct_ResultArraySQLInfo{}
		//arayinfo.sqlhosts = list
		ResultSqlinfo.leng = C.int(leng)
		ResultSqlinfo.ers = C.CString(erMsg)
		return ResultSqlinfo
	}
	list := C.setsqlarray(C.int(len(hosts)))
	for i, host := range hosts {
		var sqlhost C.struct_SqlHost
		sqlhost.Id = C.int(host.Id)
		sqlhost.Host = C.CString(host.Host)
		sqlhost.Name = C.CString(host.Name)
		sqlhost.User = C.CString(host.User)
		sqlhost.Password = C.CString(host.Password)
		sqlhost.Database = C.CString(host.Database)
		sqlhost.Port = C.int(host.Port)
		sqlhost.Databasetype = C.int(host.Databasetype)
		C.setarrayvalue(list, C.int(i), &sqlhost)
		leng++
	}
	ResultSqlinfo = C.struct_ResultArraySQLInfo{}
	ResultSqlinfo.sqlhosts = list
	ResultSqlinfo.leng = C.int(leng)
	ResultSqlinfo.ers = C.CString(erMsg)
	return ResultSqlinfo
}

func main() {
	//sqldriver2.TestUserAuthEncoders()

	//var dbinfo C.struct_DbInfo
	//dbinfo.server = C.CString("127.0.0.1")
	//dbinfo.user = C.CString("sa")
	//dbinfo.pwd = C.CString("sql")
	//dbinfo.db = C.CString("testdb")
	//dbinfo.port = C.int(3688)

	InitCliModule()
	var sqlhost C.struct_SqlHost
	sqlhost.Host = C.CString("127.0.0.1")
	//sqlhost.User = C.CString("sa")
	sqlhost.Password = C.CString("sql")
	sqlhost.Database = C.CString("testdb")
	sqlhost.Port = C.int(3688)
	sqlhost.Databasetype = 1
	sqlhost.Id = 0
	sqlhost.Name = C.CString("127.0.0.12")
	var lna C.struct_ResultInfo
	DLStore(sqlhost, &lna)

	fmt.Println("")
	fmt.Println("id" + C.GoString(lna.strId))
	fmt.Println("err =" + C.GoString(lna.ers))

}
